#pragma once

#include <mutex>
#include <queue>
#include <atomic>
#include <chrono>
#include <type_traits>
#include <condition_variable>

/**
 * 多生产者多消费队列
 */
template <typename T>
class MPMCQueue {
 public:
  MPMCQueue() : shutdown_(false) {}

  /**
   * 获取等待队列的大小
   */
  size_t GetSize() {
    std::lock_guard<std::mutex> lock(queue_mutex_);
    return queue_.size();
  }

  /**
   * 队列是否为空
   */
  bool IsEmpty() {
    std::lock_guard<std::mutex> lock(queue_mutex_);
    return queue_.empty();
  }

  /**
   * 入队
   */
  void Enqueue(const T &value) {
    {
      std::unique_lock<std::mutex> lock(queue_mutex_);

      if (shutdown_) {
        return;
      }

      queue_.push(std::move(value));
    }
    condition_.notify_one();
  }

  /**
   * 出队
   * 如果队空或者队列销毁中返回失败
   * @param value   [out] 原队头元素
   */
  bool Dequeue(T &value) {
    std::unique_lock<std::mutex> lock(queue_mutex_);

    if (queue_.empty() || shutdown_) {
      return false;
    }

    value = queue_.front();
    queue_.pop();

    return true;
  }

  /**
   * 等待出队
   * 队空并且不是销毁中 等待元素入队
   * @param value   [out] 原队头元素
   */
  void DequeueWait(T &value) {
    std::unique_lock<std::mutex> lock(queue_mutex_);

    while (queue_.empty() && !shutdown_) {
      condition_.wait(lock);
    }

    if (queue_.empty() || shutdown_) {
      return;
    }

    value = queue_.front();
    queue_.pop();
  }

  /**
   * 指定等待时间出队
   * 如果等待时间到了条件不满足就返回失败
   * @param value   [out] 原队头元素
   * @param wait_duration 等待时间
   */
  bool DequeueWaitFor(T &value, std::chrono::seconds wait_duration) {
    std::unique_lock<std::mutex> lock(queue_mutex_);

    if (!condition_.wait_for(lock, wait_duration,
                             [this]() { return !this->queue_.empty(); })) {
      return false;
    }

    if (queue_.empty() || shutdown_) {
      return false;
    }

    value = queue_.front();
    queue_.pop();

    return true;
  }

  /**
   * 清空所有队列元素 并且设置队列为不可用状态
   */
  void Cancel() {
    std::unique_lock<std::mutex> lock(queue_mutex_);

    while (!queue_.empty()) {
      T &value = queue_.front();

      // 元素销毁
      DeleteQueuedObject(value);

      queue_.pop();
    }
    shutdown_ = true;
    condition_.notify_all();
  }

 private:
  template <typename E = T>
  std::enable_if_t<std::is_pointer_v<E>> DeleteQueuedObject(E &obj) {
    delete obj;
  }

  template <typename E = T>
  std::enable_if_t<!std::is_pointer_v<E>> DeleteQueuedObject(const E &) {}

 private:
  std::mutex queue_mutex_;             // 队列互斥锁
  std::queue<T> queue_;                // 队列
  std::condition_variable condition_;  // 条件
  std::atomic<bool> shutdown_;         // 结束标志
};
